#import "draw.h"

/*
 * This Ruler should really look at the NXMeasurementUnit default
 * and, based on that, use the proper units (whether centimeters
 * or inches) rather than just always using inches.
 */

#define LINE_X (15.0)
#define WHOLE_HT (10.0)
#define HALF_HT (8.0)
#define QUARTER_HT (4.0)
#define EIGHTH_HT (2.0)
#define NUM_X (3.0)

#define WHOLE (72)
#define HALF (WHOLE/2)
#define QUARTER (WHOLE/4)
#define EIGHTH (WHOLE/8)

@implementation Ruler

+ (NXCoord)width
{
    return 23.0;
}

- initFrame:(const NXRect *)frameRect
{
    [super initFrame:frameRect];
    [self setFont:[Font systemFontOfSize:8.0 matrix:NX_IDENTITYMATRIX]];
    startX = [[self class] width];
    return self;
}

- setFlipped:(BOOL)flag
/*
 * This view doesn't work when it's "flipped," so we
 * recycle that idea to mean whether the ruler has "0" at
 * the origin of the view or not.
 */
{
    flipped = flag ? YES : NO;
    return self;
}

- setFont:(Font *)aFont
{
    NXCoord as, lh;

    font = aFont;
    NXTextFontInfo(aFont, &as, &descender, &lh);
    if (descender < 0.0) descender = -1.0 * descender;

    return self;
}

- drawHorizontal:(const NXRect *)rects
{
    NXRect line, clip;
    int curPos, last, mod, i, j;

    PSsetgray(NX_LTGRAY);
    NXRectFill(rects);

    if (lastlp >= rects->origin.x && lastlp < rects->origin.x + rects->size.width) lastlp = -1.0;
    if (lasthp >= rects->origin.x && lasthp < rects->origin.x + rects->size.width) lasthp = -1.0;

    line = bounds;				/* draw bottom line */
    line.size.height = 1.0;
    PSsetgray(NX_DKGRAY);
    if (NXIntersectionRect(rects, &line)) NXRectFill(&line);

    line = bounds;
    line.size.width = 1.0;
    line.origin.x = startX - 1.0;
    if (NXIntersectionRect(rects, &line)) NXRectFill(&line);

    line = bounds;				/* draw ruler line */
    line.origin.y = LINE_X;
    line.size.height = 1.0;
    line.origin.x = startX;
    line.size.width = bounds.size.width - startX;

    PSsetgray(NX_BLACK);
    if (NXIntersectionRect(rects, &line)) NXRectFill(&line);

    clip = *rects;
    clip.origin.x = startX;
    clip.size.width = bounds.size.width - startX;
    if (NXIntersectionRect(rects, &clip)) {
	curPos = (int)(NX_X(&clip) - startX);
	last = (int)(NX_MAXX(&clip) - startX);
	if (mod = (curPos % EIGHTH)) curPos -= mod;
	if (mod = (last % EIGHTH)) last -= mod;
	line.size.width = 1.0;
	[font set];
	for (j = curPos; j <= last; j += EIGHTH) {
	    i = flipped ? bounds.size.width - j : j;
	    line.origin.x =  startX + (float)i - (flipped ? 1.0 : 0.0);
	    if (!(i % WHOLE)) {
		char buf[10];
		line.origin.y = LINE_X - WHOLE_HT;
		line.size.height = WHOLE_HT;
		NXRectFill(&line);
		PSmoveto(((float) j + NUM_X) + startX, descender + line.origin.y - 2.0);
		sprintf(buf, "%d", i / WHOLE);
		PSshow(buf);
	    } else if (!(i % HALF)) {
		line.origin.y = LINE_X - HALF_HT;
		line.size.height = HALF_HT;
		NXRectFill(&line);
	    } else if (!(i % QUARTER)) {
		line.origin.y = LINE_X - QUARTER_HT;
		line.size.height = QUARTER_HT;
		NXRectFill(&line);
	    } else if (!(i % EIGHTH)) {
		line.origin.y = LINE_X - EIGHTH_HT;
		line.size.height = EIGHTH_HT;
		NXRectFill(&line);
	    }
	}
    }

    return self;
}


- drawVertical:(const NXRect *)rects
{
    NXRect line, clip;
    int curPos, last, mod, i, j;

    PSsetgray(NX_LTGRAY);
    NXRectFill(rects);

    if (lastlp >= rects->origin.y && lastlp < rects->origin.y + rects->size.height) lastlp = -1.0;
    if (lasthp >= rects->origin.y && lasthp < rects->origin.y + rects->size.height) lasthp = -1.0;

    line = bounds;				/* draw bottom line */
    line.origin.x = bounds.size.width - 1.0;
    line.size.width = 1.0;
    PSsetgray(NX_DKGRAY);
    if (NXIntersectionRect(rects, &line)) NXRectFill(&line);

    line = bounds;				/* draw ruler line */
    line.origin.x = bounds.size.width - LINE_X - 2.0;
    line.size.width = 1.0;
    PSsetgray(NX_BLACK);
    if (NXIntersectionRect(rects, &line)) NXRectFill(&line);

    clip = *rects;
    line.origin.x++;
    if (NXIntersectionRect(rects, &clip)) {
	curPos = (int)(NX_Y(&clip));
	last = (int)(NX_MAXY(&clip));
	if (flipped) {
	    if (mod = ((int)(bounds.size.height - curPos) % EIGHTH)) curPos += mod;
	    if (mod = ((int)(bounds.size.height - last) % EIGHTH)) last += mod;
	} else {
	    if (mod = (curPos % EIGHTH)) curPos -= mod;
	    if (mod = (last % EIGHTH)) last -= mod;
	}
	line.size.height = 1.0;
	[font set];
	for (j = curPos; j <= last; j += EIGHTH) {
	    i = flipped ? bounds.size.height - j : j;
	    line.origin.y = (float)j - (flipped ? 1.0 : 0.0);
	    if (!(i % WHOLE)) {
		char buf[10];
		line.size.width = WHOLE_HT;
		NXRectFill(&line);
		PSmoveto(line.origin.x + 5.0, (float)j + (flipped ? - 10.0 : 2.0));
		sprintf(buf, "%d", i / WHOLE);
		PSshow(buf);
	    } else if (!(i % HALF)) {
		line.size.width = HALF_HT;
		NXRectFill(&line);
	    } else if (!(i % QUARTER)) {
		line.size.width = QUARTER_HT;
		NXRectFill(&line);
	    } else if (!(i % EIGHTH)) {
		line.size.width = EIGHTH_HT;
		NXRectFill(&line);
	    }
	}
    }

    return self;
}

- drawSelf:(const NXRect *) rects :(int)rectCount
{
    if (frame.size.width < frame.size.height) {
	[self drawVertical:rects];
    } else {
	[self drawHorizontal:rects];
    }
    return self;
}


#define SETPOSITION(value) (isVertical ? (rect.origin.y = value - (absolute ? 0.0 : 1.0)) : (rect.origin.x = value + (absolute ? 0.0 : startX)))
#define SETSIZE(value) (isVertical ? (rect.size.height = value) : (rect.size.width = value))
#define SIZE (isVertical ? rect.size.height : rect.size.width)

- doShowPosition:(NXCoord)lp :(NXCoord)hp absolute:(BOOL)absolute
{
    NXRect rect;
    BOOL isVertical = (frame.size.width < frame.size.height);

    rect = bounds;

    if (!absolute && !isVertical) {
	if (lp < 0.0) lp -= startX;
	if (hp < 0.0) hp -= startX;
    }

    SETSIZE(1.0);
    lastlp = SETPOSITION(lp);
    NXHighlightRect(&rect);
    lasthp = SETPOSITION(hp);
    NXHighlightRect(&rect);

    return self;
}

- showPosition:(NXCoord)lp :(NXCoord)hp
{
    [self lockFocus];
    if (notHidden) [self doShowPosition:lastlp :lasthp absolute:YES];
    [self doShowPosition:lp :hp absolute:NO];
    [self unlockFocus];
    notHidden = YES;
    return self;
}

- hidePosition
{
    if (notHidden) {
	[self lockFocus];
	[self doShowPosition:lastlp :lasthp absolute:YES];
	[self unlockFocus];
	notHidden = NO;
    }
    return self;
}

@end
